package logs

import (
	"fmt"
	"os"
	"path"
	"qingbing/xztools/chans"
	"qingbing/xzutils/files"
	"qingbing/xzutils/strs"
)

// 日志级别类型
type LogLevel uint8

/**
 * 通过返回 level 代表字符串
 */
func (ll LogLevel) String() string {
	switch ll {
	case LOG_LEVEL_DEBUG:
		return "DEBUG"
	case LOG_LEVEL_INFO:
		return "INFO"
	case LOG_LEVEL_LOG:
		return "LOG"
	case LOG_LEVEL_WARN:
		return "WARN"
	case LOG_LEVEL_ERROR:
		return "ERROR"
	case LOG_LEVEL_FATAL:
		return "FATAL"
	default:
		return "UNKNOWN"
	}
}

// 定义日志级别
const (
	LOG_LEVEL_DEBUG LogLevel = iota
	LOG_LEVEL_INFO
	LOG_LEVEL_LOG
	LOG_LEVEL_WARN
	LOG_LEVEL_ERROR
	LOG_LEVEL_FATAL
)

// 管道中存储的三数据结构
type LogData struct {
	Level LogLevel
	Data  any
}

// 日志记录组件
type FileRecord struct {
	// 日志标识
	ID string
	// 目录属性
	Dir      string
	Filename string
	// 数据属性
	MinLogLevel LogLevel                          // 接收的最小日志级别
	Formatter   func(data *chans.ChanData) string // 日志数据格式化回调
	// 日志文件控制属性
	MaxSize int64
	// 继承通道的功能
	// 私有属性
	currentSize int64
	logFile     string
	fp          *os.File
	ch          *chans.Chan
}

/**
 * 向管道推送数据
 */
func (fr *FileRecord) PutToPipe(level LogLevel, data any) error {
	// 日志级别小于设置级别， 不记录数据
	if level < fr.MinLogLevel {
		return nil
	}

	// 向通道推送数据
	fr.ch.Append(&LogData{
		Level: level,
		Data:  data,
	})

	return nil
}

/**
 * 消费管道数据
 */
func (fr *FileRecord) consume(data *chans.ChanData) {
	/**
	 * 日志类型判断
	 */
	logData, ok := data.Data.(*LogData)
	if !ok {
		return
	}

	/**
	 * 判断文件是否超出大小限制
	 */
	if fr.MaxSize > 0 && fr.currentSize > fr.MaxSize {
		if fr.fp != nil {
			fr.fp.Close()
			fr.fp = nil
		}
		// 文件超出大小进行切片
		files.CutFile(fr.logFile)
	}

	/**
	 * 打开日志文件句柄
	 */
	if fr.fp == nil {
		stat, err := os.Stat(fr.logFile)
		if err != nil {
			fr.currentSize = 0
		} else {
			fr.currentSize = stat.Size()
		}
		if fp, err := files.OpenFile(fr.logFile); err != nil {
			return
		} else {
			fr.fp = fp
		}
	}

	/**
	 * 格式化并写入文件句柄
	 */
	var num int
	var err error
	if fr.Formatter != nil {
		num, err = fr.fp.WriteString(fr.Formatter(data) + "\n")
	} else {
		num, err = fr.fp.WriteString(fmt.Sprintf("[%-7s]%s %v\n", logData.Level.String(), data.Time.Format("2006-01-02 15:04:05.000000"), logData.Data))
	}
	if err == nil && fr.MaxSize > 0 {
		fr.currentSize += int64(num)
	}
}

/**
 * 向管道推送 debug 数据
 */
func (fr *FileRecord) PutDebug(data any) error {
	return fr.PutToPipe(LOG_LEVEL_DEBUG, data)
}

/**
 * 向管道推送 info 数据
 */
func (fr *FileRecord) PutInfo(data any) error {
	return fr.PutToPipe(LOG_LEVEL_INFO, data)
}

/**
 * 向管道推送 log 数据
 */
func (fr *FileRecord) PutLog(data any) error {
	return fr.PutToPipe(LOG_LEVEL_LOG, data)
}

/**
 * 向管道推送 warn 数据
 */
func (fr *FileRecord) PutWarn(data any) error {
	return fr.PutToPipe(LOG_LEVEL_WARN, data)
}

/**
 * 向管道推送 error 数据
 */
func (fr *FileRecord) PutError(data any) error {
	return fr.PutToPipe(LOG_LEVEL_ERROR, data)
}

/**
 * 向管道推送 fatal 数据
 */
func (fr *FileRecord) PutFatal(data any) error {
	return fr.PutToPipe(LOG_LEVEL_FATAL, data)
}

/**
 * 启动组件
 */
func (fr *FileRecord) Run() error {
	fr.ch = &chans.Chan{
		ID:          fr.ID,
		ChanSize:    10,
		FuncConsume: fr.consume,
	}
	if fr.ID == "" {
		fr.logFile = fr.Filename
	} else {
		fr.logFile = path.Join(fr.Dir, strs.Join("_", fr.ID, fr.Filename))
	}
	return fr.ch.Run()
}

/**
 * 关闭日志资源句柄
 */
func (fr *FileRecord) Close() {
	if fr.ch != nil {
		fr.ch.Close()
	}
	if fr.fp != nil {
		fr.fp.Close()
	}
}

/**
 * 文件记录配置数据结构
 */
type ConfFileRecord struct {
	// 日志标识
	ID string
	// 目录属性
	Dir      string
	Filename string
	// 数据属性
	MinLogLevel LogLevel                          // 接收的最小日志级别
	Formatter   func(data *chans.ChanData) string // 日志数据格式化回调
	// 日志文件控制属性
	MaxSize int64
}

/**
 * 新建 FileLog
 */
func NewFileRecord(conf *ConfFileRecord) *FileRecord {
	fr := &FileRecord{
		ID:          conf.ID,
		Dir:         conf.Dir,
		Filename:    conf.Filename,
		MinLogLevel: conf.MinLogLevel,
		MaxSize:     conf.MaxSize,
		Formatter:   conf.Formatter,
	}
	fr.Run()
	return fr
}
